package br.com.persist.core.command;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import br.com.persist.api.Command;
import br.com.persist.api.PersistentContext;
import br.com.persist.api.RuntimeExecutionException;
import br.com.persist.api.VolatileContext;
import br.com.persist.core.Function;
import br.com.persist.core.command.parameter.ParameterExtractor;

public class FunctionCallCommand implements Command {

	private List<ParameterExtractor> parameters = new ArrayList<ParameterExtractor>();
	private Function function;

	public FunctionCallCommand(Function function) {
		super();
		this.function = function;
	}

	public Object execute(VolatileContext volatileContext,
			PersistentContext persistentContext)
			throws RuntimeExecutionException {
		VolatileContext subContext = volatileContext.createSubContext();

		try {
			copyParameters(parameters, function.getParameterNames(),
					volatileContext, subContext, persistentContext);

			return function.execute(subContext, persistentContext);
		} catch (RuntimeExecutionException e) {
			e.registerCommand(this);
			throw e;
		} finally {
			volatileContext.destroySubContext(subContext);
		}
	}

	protected void copyParameters(List<ParameterExtractor> parameterExtractors,
			List<String> parameterNames, VolatileContext volatileContext,
			VolatileContext subContext, PersistentContext persistentContext)
			throws RuntimeExecutionException {
		if (parameterNames.size() != parameterExtractors.size()) {
			throw new RuntimeExecutionException("Expected "
					+ parameterNames.size() + " but found "
					+ parameterExtractors.size());
		}

		Iterator<String> i = parameterNames.iterator();
		for (ParameterExtractor parameterExtractor : parameterExtractors) {
			Object value = parameterExtractor.getValue(volatileContext,
					persistentContext);
			subContext.update(i.next(), value);
		}
	}
}
